import { AnalyticsData } from "../../app_base/module/publisher/IAnalyticsAble";
import { IView } from "../../app_base/module/view/IView";
import { ViewAnalyticsInfo } from "../../app_base/module/view/ViewManager";
import { app } from "../../base";
import { CCPViewConfig } from "./CCPViewLoader";
import { ViewLevel } from "./Scene";
import { ViewControl } from "./ViewControl";

const { ccclass, property, menu } = cc._decorator;

@ccclass()
@menu("基础视图/View")
/**
 * cocos引擎下的视图管理类
 */
export class View extends cc.Component implements IView {
    /**
     * View类的Class名称
     */
    public static type: string = "View";
    /**
     * 视图名称，加载View时自动赋值。
     */
    public name: string;
    /**
     * 视图配置，加载View时自动赋值。
     */
    public config: CCPViewConfig;

    /**
     * 视图统计信息
     */
    public analyticsInfo: ViewAnalyticsInfo;

    /**
     * 视图所在bundle，加载View时自动赋值。
     */
    public bundle: cc.AssetManager.Bundle;

    /**
     * 视图的控制器，在Scene中创建。
     */
    protected _control: ViewControl;
    /**
     * 视图顶层的遮罩，辅助touchEnabled完成当前视图的可交互性。
     */
    private _mask: cc.Node;
    /**
     * 是否可以交互
     */
    private _touchEnabled: boolean = true;

    /**
     * 是否在舞台中显示着
     */
    public get inStage(): boolean {
        return this.node.activeInHierarchy;
    }

    /**
     * 视图是否响应交互
     */
    public get touchEnabled(): boolean {
        return this._touchEnabled;
    }

    /**
     * 视图是否响应交互
     */
    public set touchEnabled(value: boolean) {
        this._touchEnabled = value;
        if (value && !this._mask) {
            return;
        }
        if (!this._mask) {
            this._mask = new cc.Node();
            this._mask.name = "mask";
            this._mask.width = cc.winSize.width;
            this._mask.height = cc.winSize.height;
            this._mask.addComponent(cc.BlockInputEvents);
            // this.node.addChild(this.mask);
            this.node.addChild(this._mask, ViewLevel.TOPUP);
        }
        this._mask.active = !value;
    }

    /**
     * 添加其他视图
     * @param view 其他视图
     * @param index 视图索引
     */
    public addView(view: View, index?: number): void {
        view.setControl(this._control);
        this.node.addChild(view.node, index);
        app.viewManager.event("open", view);
        app.viewManager.reportViewAnalytics("open", view);
    }

    /**
     * 移除子视图
     * @param view 子视图
     * @param destroy 是否销毁，默认为true
     * @param closeEventParams 视图close事件的参数
     */
    public removeView(view: View, destroy: boolean = true, ...closeEventParams: any[]): void {
        if (view.parentView == this) {
            destroy && view.node.destroyAllChildren();
            view.node.removeFromParent(destroy);
            app.viewManager.event("close", view, ...closeEventParams);
            app.viewManager.reportViewAnalytics("close", view);
        }
    }

    /**
     * 设置主场景所创建的Control
     * @param control 
     */
    public setControl(control: ViewControl): void {
        this._control = control;
    }

    protected control<T extends ViewControl>(): T {
        return this._control as T;
    }

    /**
     * 实例化后显示到舞台之后调用,缓存视图重新显示时也会调用。
     * @param data 
     */
    public setData(...data: any[]): void {

    }

    /**
     * 上报当前视图统计数据
     * @param event open和close以外的统计名称，名称必须为全小写，无下划线之外的其他特殊字符。
     * @param data 上报附带信息，可选参数，为空时会尝试从getAnalyticsDataOnViewEvent获取附带信息。
     */
    protected _report(event: string, data?: AnalyticsData): void {
        if (event == "open" || event == "close") return;
        app.viewManager.reportViewAnalytics(event, this, data);
    }

    /**
     * 播放BG音乐
     * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
     * @param loop 是否循环 默认为true
     * @param onFinishCallBack 单次播放结束后的回调箭头函数（cocos的锅)
     * @returns 背景音乐的audioId
     * 
     * @example
     * _playBG("https://server.com/game/music.mp3")
     * _playBG("main://path/to/music")
     * _playBG("resources://path/to/music")
     */
    protected async _playBG(path: string, loop?: boolean, onFinishCallBack?: () => void) {
        let clip: cc.AudioClip = await app.res.load(path);
        if (clip) {
            let id = cc.audioEngine.playMusic(clip, loop);
            if (onFinishCallBack) {
                cc.audioEngine.setFinishCallback(id, onFinishCallBack);
            }
            return id;
        }
        return null;
    }

    /**
     * 播放音效
     * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
     * @param loop 是否循环 默认为false
     * @param onFinishCallBack 单次播放结束后的回调箭头函数（cocos的锅)
     * @returns 音效的audioId
     */
    protected async _playEffect(path: string, loop?: boolean, onFinishCallBack?: () => void): Promise<number> {
        let clip: cc.AudioClip = await app.res.load(path);
        if (clip) {
            let id = cc.audioEngine.playEffect(clip, loop);
            if (onFinishCallBack) {
                cc.audioEngine.setFinishCallback(id, onFinishCallBack);
            }
            return id;
        }
        return null;
    }

    /**
     * 播放激励视频的快捷方式。
     * @param from 可选参数，可以附带广告行为的来源，以及其他需要统计的信息
     * @param stopRecorder 可选参数，是否暂停录屏， 默认为true
     */
    protected _showRewardVideo(from?: { action?: string, [key: string]: string | boolean | number }, stopRecorder: boolean = true): Promise<boolean> {
        return new Promise<boolean>((resolve) => {
            stopRecorder && app.publisher.record && app.publisher.record.pauseRecord();
            app.viewManager.closeTouch();
            from = from || {};
            from.view = this.analyticsInfo ? this.analyticsInfo.name : this.name;
            app.publisher.ad.showRewardVideo(from, this, () => {
                app.viewManager.openTouch();
                stopRecorder && app.publisher.record && app.publisher.record.resumeRecord();
                resolve(true);
            }, () => {
                app.viewManager.openTouch();
                stopRecorder && app.publisher.record && app.publisher.record.resumeRecord();
                resolve(false);
            }, 0);
        });
    }

    /**
     * 分享录屏视频
     * @param title 视频标题 
     * @param topics 视频话题列表
     */
    protected _shareRecord(title: string, topics: string[]): Promise<boolean> {
        return new Promise<boolean>((resolve) => {
            let path = app.publisher.record.videoPath;
            if (path) {
                app.publisher.share.shareVideo([title, topics, path], this, () => {
                    resolve(true);
                }, () => {
                    resolve(false);
                });
            } else {
                resolve(false);
            }
        });
    }

    /**
     * 父级视图
     */
    public get parentView(): View {
        if (this.node.parent) {
            return this.node.parent.getComponent(View);
        }
        return null;
    }

    /**
     * 关闭视图,根据视图是否缓存选择是否销毁视图
     * @param closeEventParams 视图close事件的参数
     */
    public close(...closeEventParams: any[]): void {
        this.removeFromParent(!this.config.cache, ...closeEventParams);
    }

    /**
     * 从父级移除视图
     * @param destroy 是否销毁，默认为true
     * @param closeEventParams 视图close事件的参数
     */
    public removeFromParent(destroy: boolean = true, ...closeEventParams: any[]): void {
        if (this.parentView) {
            this.parentView.removeView(this, destroy, ...closeEventParams);
        }
    }

    /**
     * 直接销毁
     */
    public allDestroy(): void {
        if (this.node && cc.isValid(this.node)) {
            this.node.parent = null;
            this.node.cleanup();
        }
    }

    public getAnalyticsDataOnViewEvent(event: "open" | "close" | string): AnalyticsData {
        return {};
    }

    /**
     * 加载与当前ui下同bundle的资源
     * @param name 资源名称
     * @param assetType 资源类型
     * @returns 返回加载的资源
     */
    public async loadRes<T extends cc.Asset>(name: string, assetType?: new () => T): Promise<T>;
    public async loadRes<T extends cc.Asset>(name: string, assetType?: new () => T): Promise<T | T[]>;
    public async loadRes<T extends cc.Asset>(name: string, assetType?: new () => T): Promise<T | T[]> {
        let bundle = app.res.parsePath(this.config.path).bundle
        return app.res.load(`${bundle}://Prefab/${name}`, assetType);
    }

    /**
     * 同步加载与当前ui下同bundle的资源
     * @param name 资源名称
     * @param assetType 资源类型
     * @returns 返回加载的资源
     */
    public loadResSync<T extends cc.Asset>(name: string, assetType?: new () => T)
    public loadResSync<T extends cc.Asset>(name: string, assetType?: new () => T)
    public loadResSync<T extends cc.Asset>(name: string, assetType?: new () => T): T {
        const bundleName = app.res.parsePath(this.config.path).bundle;
        const bundle: cc.AssetManager.Bundle = cc.assetManager.getBundle(bundleName);
        return bundle.get(name, assetType) as T;
    }

    public loadSpriteFrame(name: string): Promise<cc.SpriteFrame> {
        let bundle = app.res.parsePath(this.config.path).bundle
        return app.res.load(`${bundle}://Prefab/${name}`, cc.SpriteFrame);
    }


    public getChildByPath<T extends cc.Node>(name: string, parent?: cc.Node): T {
        parent = parent ? parent : this.node
        if (name == null) return parent as T
        let paths: string[] = name.split('.')
        let target = parent;
        for (let index = 0; index < paths.length; index++) {
            let childName = paths[index]
            if (target == null) {
                let parentpath = this._getParentPath(parent)
                console.warn(parentpath + "." + name + " is not find");
                break
            }
            target = target.getChildByName(childName)
        }

        if (target)
            return target as T;

        let parentpath = this._getParentPath(parent)
        console.warn(parentpath + "." + name + " is not find");
        return null;
    }

    public _getParentPath(node) {
        if (node == null)
            return null
        let parentname = this._getParentPath(node.parent)
        if (parentname != null)
            parentname = parentname + "." + node.name
        else
            parentname = node.name
        return parentname
    }

    /*
    * 添加点击事件
    * @param path 节点路径
    * @param callback 回调函数
    * @param parent 父节点
    * @param isScale 是否缩放
    * @param clickSound 点击音效
    */
    public addClick(path, callback, parent?, isScale = true, clickSound?) {
        let node = this.getChildByPath(path, parent)
        if (!node) return
        if (!node.getComponent(cc.Button)) {
            let btn = node.addComponent(cc.Button);
            btn.target = node;
            if (isScale) {
                btn.transition = cc.Button.Transition.SCALE;
                btn.duration = 0.1;
                btn.zoomScale = 0.85;
            }
        }
        node.targetOff(this)
        node.on('click', (event, t) => {
            if (clickSound == "")
                console.log("")
            // else if (clickSound != null)
            //     this.playMainResSound(clickSound)
            // else
            //     this.playMainResSound("click1")
            this.noFastClick(event, callback, 500);
        }, this);
        return node
    }

    /**
     * 防止快速点击的方法
     * @param event touch事件
     * @param func 回调方法
     * @param time 冷却时间,单位：毫秒
     */
    public noFastClick(event, func, time?: number) {
        if (event && event.target) {
            if (event.target._clickTime && new Date().getTime() - event.target._clickTime < (time ? time : 500)) {
                return;
            }
            event.target._clickTime = new Date().getTime();
        }
        func.call(this, event);
    }

    public addClickLong(path, startFunc, endOrCancelFunc, longFunc?, time = 1, parent?, clickSound?) {
        let timecate;
        let node = this.getChildByPath(path, parent)
        if (!node) {
            return;
        }
        node.scale = 1;
        node.on(cc.Node.EventType.TOUCH_START, () => {
            node["longtime"] = 0;
            node["clicktime"] = new Date().getTime();
            node.stopAllActions();
            startFunc();
            if (timecate) clearInterval(timecate);
            let timeant = () => {
                let clicktime = new Date().getTime();
                if (clicktime - node["clicktime"] > time * 1000) {
                    node["clicktime"] = clicktime;
                    node["longtime"] = 1;
                    if (timecate) clearInterval(timecate);
                    if (longFunc) longFunc();
                    if (clickSound == "")
                        console.log("")
                    // else if (clickSound != null)
                    //     this.playMainResSound(clickSound)
                    // else
                    //     this.playMainResSound("click1")
                    console.log("长按")
                }
            }
            timecate = setInterval(timeant, 200)
        }, this);

        node.on(cc.Node.EventType.TOUCH_END, () => {
            node.stopAllActions();
            if (timecate) clearInterval(timecate);
            node.scale = 1;
            endOrCancelFunc();
            let clicktime = new Date().getTime();
            node["clicktime"] = clicktime;
            // cb();
        }, this);

        node.on(cc.Node.EventType.TOUCH_CANCEL, () => {
            node.stopAllActions();
            if (timecate) clearInterval(timecate);
            node.scale = 1;
            endOrCancelFunc();
            let clicktime = new Date().getTime();
            node["clicktime"] = clicktime;
            // cb();
        }, this);
    }

}
